#include "stdafx.h"
#include "Water.h"

#include "../../Framework/ContentManager.h"

namespace MyGame {

Water::Water(Game* game, FLOAT height, int width, int length)
        : Mesh(game), height(height), width(width), length(length) {
    this->SetEffectFile("Water.fx");
}

Water::~Water(void) {
}

FLOAT Water::GetHeight() const {
    return this->height;
}

int Water::GetLength() const {
    return this->length;
}

int Water::GetWidth() const {
    return this->width;
}

IDirect3DTexture9* Water::GetReflectionTexture() const {
    return this->reflectionTexture;
}

void Water::SetReflectionTexture(IDirect3DTexture9* val) {
    this->reflectionTexture = val;
}

void Water::Draw(FLOAT timeDelta) {
    if (this->effectFile == NULL) {
        __super::Draw(timeDelta);
        return;
    }

    //D3DXVECTOR3 vLightDir = D3DXVECTOR3(1, 1, 1);
    //D3DXCOLOR vLightDiffuse = D3DXCOLOR(1, 1, 1, 1);
    //D3DXVec3Normalize(&vLightDir, &vLightDir);
    //this->effect->SetValue("g_LightDir", &vLightDir, sizeof(D3DXVECTOR3));
    //this->effect->SetValue("g_LightDiffuse", &vLightDiffuse, sizeof(D3DXVECTOR4)) ;

    //D3DXMATRIX mWorld = this->GetWorldMatrix();
    //D3DXMATRIX mWorldViewProjection = mWorld * this->GetGame()->GetCamera()->GetViewMatrix() * this->GetGame()->GetCamera()->GetProjectionMatrix();
    //this->effect->SetMatrix("g_mWorldViewProjection", &mWorldViewProjection);
    //this->effect->SetMatrix("g_mWorld", &mWorld);
    this->effect->SetFloat("g_fPhase", this->GetGame()->GetGameTime());
    this->effect->SetValue("g_vCameraPosition", this->GetGame()->GetCamera()->GetPosition(), sizeof(D3DXVECTOR3));
    D3DXMATRIX mWorld = this->GetWorldMatrix();
    D3DXMATRIX mViewProjection = this->GetGame()->GetCamera()->GetViewMatrix() * this->GetGame()->GetCamera()->GetProjectionMatrix();
    this->effect->SetMatrix("g_mViewProj", &mViewProjection);
    this->effect->SetMatrix("g_mWorld", &mWorld);
    this->effect->SetTechnique("RenderWater");
    this->GetGame()->GetGraphicsDevice()->SetRenderState(D3DRS_ALPHABLENDENABLE, true);
    this->GetGame()->GetGraphicsDevice()->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
    this->GetGame()->GetGraphicsDevice()->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
    this->GetGame()->GetGraphicsDevice()->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
    this->GetGame()->GetGraphicsDevice()->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
    __super::Draw(timeDelta);
    this->GetGame()->GetGraphicsDevice()->SetRenderState(D3DRS_ALPHABLENDENABLE, false);
}

void Water::Initialize() {
}

void Water::GenerateIndices(DWORD* indices) {
    int index = 0;
    for (int z = 0; z < this->length - 1; z++) {
        for (int x = 0; x < this->width - 1; x++) {
            DWORD vertexIndex = x + z * this->width;
            indices[index++] = vertexIndex;
            indices[index++] = vertexIndex + this->width;
            indices[index++] = vertexIndex + this->width + 1;
            indices[index++] = vertexIndex;
            indices[index++] = vertexIndex + this->width + 1;
            indices[index++] = vertexIndex + 1;
        }
    }
}

void Water::GenerateModel() {
    int numFaces = (this->length - 1) * (this->width - 1) * 2;
    int numVertices = this->length * this->width;

    ID3DXMesh* mesh = NULL;

    D3DXCreateMeshFVF(
        numFaces,
        numVertices,
        D3DXMESH_MANAGED | D3DXMESH_32BIT,
        Vertex::FVF,
        this->GetGame()->GetGraphicsDevice(),
        &mesh);

    Vertex* vertices = NULL;
    mesh->LockVertexBuffer(0, (void**)&vertices);
    this->GenerateVertices(vertices);
    mesh->UnlockVertexBuffer();

    DWORD* indices = 0;
    mesh->LockIndexBuffer(0, (void**)&indices);
    this->GenerateIndices(indices);
    mesh->UnlockVertexBuffer();

    DWORD* attributeBuffer = 0;
    mesh->LockAttributeBuffer(0, &attributeBuffer);
    for (int i = 0; i < numFaces; i++) {
        attributeBuffer[i] = 0;
    }
    mesh->UnlockAttributeBuffer();

    DWORD* adjacencyBuffer = new DWORD[mesh->GetNumFaces() * 3];
    mesh->GenerateAdjacency(0.0f, adjacencyBuffer);
    D3DXComputeNormals(mesh, adjacencyBuffer);
    mesh->OptimizeInplace(
        D3DXMESHOPT_ATTRSORT | D3DXMESHOPT_COMPACT | D3DXMESHOPT_VERTEXCACHE,
        adjacencyBuffer,
        0, 0, 0);

    this->mesh = new D3DXMesh(mesh);
}

void Water::GenerateVertices(Vertex* vertices) {
    float halfWidth = ((FLOAT)this->width - 1.0f) / 2.0f;
    float halfLength = ((FLOAT)this->length - 1.0f) / 2.0f;
    for ( int x = 0; x < this->width; x++ ) {
        for ( int z = 0; z < this->length; z++ ) {
            vertices[z * this->width + x] = Vertex(
                                                (FLOAT)x - halfWidth,
                                                this->height,
                                                (FLOAT)z - halfLength,
                                                0, 1, 0,
                                                (FLOAT)x / (this->width - 1),
                                                (FLOAT)z / (this->length - 1));
        }
    }
}

void Water::LoadContent() {
    __super::LoadContent();

    this->GenerateModel();

    if (this->effectFile == NULL) {
        return;
    }
    D3DXCOLOR colorMtrlDiffuse(1.0f, 1.0f, 1.0f, 1.0f);
    D3DXCOLOR colorMtrlAmbient(0.35f, 0.35f, 0.35f, 0);
    D3DXCOLOR colorMtrlSpecular(1.0f, 1.0f, 1.0f, 1.0f);

    this->mesh->GetMaterials()->push_back(d3d::WHITE_MTRL);

    this->textureWater = this->GetGame()->GetContentManager()->LoadVolumeTexture("NoiseVolume.dds");
    this->mesh->GetTextures()->push_back(this->textureWater);

    this->effect->SetFloat("g_fAmplitude", 0.01f * this->height);
    this->effect->SetTexture("g_NoiseTexture", this->textureWater);
    this->effect->SetTexture("g_ReflectionTexture", this->reflectionTexture);
}

}
